package com.choosefine.paycenter.pay.api.handler;

import com.abc.pay.client.Constants;
import com.abc.pay.client.TrxException;
import com.abc.pay.client.ebus.PaymentResult;
import com.alibaba.fastjson.JSON;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.SendResult;
import com.aliyun.openservices.ons.api.transaction.TransactionProducer;
import com.choosefine.paycenter.account.dto.OrderDto;
import com.choosefine.paycenter.account.model.Recharge;
import com.choosefine.paycenter.account.model.ShiXiaoBaoTrx;
import com.choosefine.paycenter.account.service.ShiXiaoBaoTrxService;
import com.choosefine.paycenter.common.enums.FundFlowDirection;
import com.choosefine.paycenter.common.enums.PayStatus;
import com.choosefine.paycenter.common.enums.RechargeStatus;
import com.choosefine.paycenter.common.utils.ByteArrayStringUtils;
import com.choosefine.paycenter.common.utils.SerialNumberUtils;
import com.choosefine.paycenter.common.utils.UriUtils;
import com.choosefine.paycenter.pay.api.PayService;
import com.choosefine.paycenter.pay.api.RechargeService;
import com.choosefine.paycenter.pay.constants.PayConstants;
import com.choosefine.paycenter.pay.dispatcher.TransferQueryDispatcher;
import com.choosefine.paycenter.pay.dto.PayBackDto;
import com.choosefine.paycenter.pay.dto.PayOrderDto;
import com.choosefine.paycenter.pay.dto.PayOrderMessageDto;
import com.choosefine.paycenter.pay.dto.TransferQueryDto;
import com.choosefine.paycenter.pay.handler.TransferQueryHandler;
import com.choosefine.paycenter.pay.model.Payment;
import com.choosefine.paycenter.pay.model.PaymentToTradeOrder;
import com.choosefine.paycenter.pay.model.TradeOrder;
import com.choosefine.paycenter.pay.mq.PayLocalTransactionExecuter;
import com.choosefine.paycenter.pay.service.ChannelService;
import com.choosefine.paycenter.pay.service.PaymentService;
import com.choosefine.paycenter.pay.service.PaymentToTradeOrderService;
import com.choosefine.paycenter.pay.service.TradeOrderService;
import com.choosefine.paycenter.pay.vo.TransferQueryVo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * Comments：支付回调处理抽象类(非余额支付使用)
 * Author：Jay Chang
 * Create Date：2017/4/17
 * Modified By：
 * Modified Date：
 * Why & What is modified：
 * Version：v1.0
 */
@Slf4j
@Component
public abstract class AbstractPayBackHandler implements PayBackHandler{
    protected PayService payService;

    @Autowired
    private PaymentService paymentService;//这里应该理解为支付订单服务

    @Autowired
    private RechargeService rechargeService;//这里应该理解为充值订单服务

    @Autowired
    private ShiXiaoBaoTrxService shiXiaoBaoTrxService;

    @Autowired
    private ChannelService channelService;

    @Autowired
    private TradeOrderService tradeOrderService;

    @Autowired
    private PaymentToTradeOrderService paymentToTradeOrderService;

    @Autowired
    private SerialNumberUtils serialNumberUtils;

    @Autowired
    private TransactionProducer producer;

    @Autowired
    private PayLocalTransactionExecuter payLocalTransactionExecuter;//处理本地事务（创建支付单、更改支付单状态为支付成功，更改支付单状态为支付失败）成功后，才会发送消息

    @Autowired
    TransferQueryDispatcher transferQueryDispatcher;
    @Value("${aliyun.ons.topic.topicPay}")
    private String topicPaycenterPay;

    private static final String RETURN_URL_PARAMS1 ="success={success}&message={message}&bizzSys={bizzSys}&bizzSn={bizzSn}&amount={amount}";

    private static final String RETURN_URL_PARAMS2 ="success={success}&message={message}&amount={amount}";

    /**
     * 初始化（必须初始化payService）
     */
    public abstract void init();

    /**
     * 处理页面同步回调（不处理状态修改，只重定向客户端浏览器到reutnrUrl）
     */
    public String doProcessReturn(final PayBackDto payBackDto){
        final PayOrderDto payOrderDto = payService
                .getPayOrderFromBackParam(payBackDto.getParams(), payBackDto.getPayType());
        String returnUrl = null;
        boolean isPaySn = false;
        boolean isRechargeSn = false;
        Payment payment = null;
        Recharge recharge = null;
        if(isPaySn = serialNumberUtils.isPaySn(payOrderDto.getOrderId())){
            payment = paymentService.findByPaySn(payOrderDto.getOrderId());
            returnUrl = payment.getReturnUrl();
        }
        if(isRechargeSn = serialNumberUtils.isRechargeSn(payOrderDto.getOrderId())){
            returnUrl = rechargeService.findReturnUrlByRechargeSn(payOrderDto.getOrderId());
        }
        if(StringUtils.isEmpty(returnUrl)){
            throw new RuntimeException("["+ payOrderDto.getOrderId()+"]支付已经成功，但由于未设置returnUrl无法跳转到支付成功页面");
        }
        //验签
        boolean verifySignFlag = payService.verifySign(payBackDto.getParams(),getSign(payBackDto.getParams(), payBackDto.getPayType()), payBackDto.getPayType(), payBackDto.getNotifyType());
        //url参数
        Map<String,String> params = Maps.newHashMap();
        params.put("success","false");
        params.put("message","");
        if(isPaySn){//是支付订单
            params.put("bizzSys",payment.getBizzSys().toString());
            params.put("bizzSn",payment.getBizzSn());
            params.put("amount",String.format("%.2f",payOrderDto.getAmount()));
          if(verifySignFlag){//若验签成功
                if(isPaySuccess(payBackDto.getParams(), payBackDto.getPayType())){
                    //建行B2B支付回调特殊处理（建行B2B支付服务器通知回调有点不稳定）
                    if("ccb".equalsIgnoreCase(payBackDto.getChannel()) && "b2b_nb".equalsIgnoreCase(payBackDto.getPayType())){
                        doProcessSuccess(payBackDto);
                    }
                    params.put("success","true");
                    String returnUrlParamsStr = UriComponentsBuilder.fromUriString(RETURN_URL_PARAMS1).buildAndExpand(params).toUriString();
                    return returnUrl + "?"+returnUrlParamsStr;
                }else{
                    //建行B2B支付回调特殊处理（建行B2B支付服务器通知回调有点不稳定）
                    if("ccb".equalsIgnoreCase(payBackDto.getChannel()) && "b2b_nb".equalsIgnoreCase(payBackDto.getPayType())){
                        doProcessFailure(payBackDto);
                    }
                    params.put("message",UriUtils.getInstance().encode("支付失败","UTF-8"));
                    String returnUrlParamsStr = UriComponentsBuilder.fromUriString(RETURN_URL_PARAMS1).buildAndExpand(params).toUriString();
                    return returnUrl + "?"+ returnUrlParamsStr;
                }
          }else{
              params.put("message",UriUtils.getInstance().encode("回调参数验签失败", "UTF-8"));
              String returnUrlParamsStr = UriComponentsBuilder.fromUriString(RETURN_URL_PARAMS1).buildAndExpand(params).toUriString();
              return returnUrl+"?"+ returnUrlParamsStr;
          }
        }else{//是充值订单
            params.put("amount",String.format("%.2f",payOrderDto.getAmount()));
            if(verifySignFlag){//若验签成功
                if(isPaySuccess(payBackDto.getParams(), payBackDto.getPayType())){
                    //建行B2B支付回调特殊处理（建行B2B支付服务器通知回调有点不稳定）
                    if("ccb".equalsIgnoreCase(payBackDto.getChannel()) && "b2b_nb".equalsIgnoreCase(payBackDto.getPayType())){
                        doProcessSuccess(payBackDto);
                    }
                    params.put("success","true");
                    return returnUrl + "?"+UriComponentsBuilder.fromUriString(RETURN_URL_PARAMS2).buildAndExpand(params).toUriString();
                }else{
                    //建行B2B支付回调特殊处理（建行B2B支付服务器通知回调有点不稳定）
                    if("ccb".equalsIgnoreCase(payBackDto.getChannel()) && "b2b_nb".equalsIgnoreCase(payBackDto.getPayType())){
                        doProcessFailure(payBackDto);
                    }
                    params.put("message",UriUtils.getInstance().encode("充值失败","UTF-8"));
                    return returnUrl + "?"+UriComponentsBuilder.fromUriString(RETURN_URL_PARAMS2).buildAndExpand(params).toUriString();
                }
            }else{
                params.put("message",UriUtils.getInstance().encode("回调参数验签失败","UTF-8"));
                return returnUrl + "?"+UriComponentsBuilder.fromUriString(RETURN_URL_PARAMS2).buildAndExpand(params).toUriString();
            }
        }
    }

    /**
     * 处理异步回调(目前规定通过异步回调来更改支付单、充值单、账单状态。【然后通过消息中间件，将消息传给账户、账单模块，通过消息监听器处理更改账户余额，余额日志更新】)
     */
    public String doProcessNotify(final PayBackDto payBackDto) {
        if (payBackDto.getChannel().toUpperCase().equals("ABC")) {
            return ABCBankNotify(payBackDto);
        }
        //验签
        boolean verifySignFlag = payService.verifySign(payBackDto.getParams(),getSign(payBackDto.getParams(), payBackDto.getPayType()), payBackDto.getPayType(), payBackDto.getNotifyType());
        //若验签成功
        if(verifySignFlag){
            if(isPaySuccess(payBackDto.getParams(), payBackDto.getPayType())) {
                doProcessSuccess(payBackDto);
            }else{
                doProcessFailure(payBackDto);
            }
            return "success";
        }else {
            return "failure";
        }
    }

    /**
     * 从回调参数中获取签名参数值
     * @param params
     * @return
     */
    protected abstract String getSign(Map<String, String> params,String payType);

    /**
     * 从回调参数中获取支付成功标志参数，判断是否支付成功
     * @param params
     * @return
     */
    protected abstract boolean isPaySuccess(Map<String, String> params,String payType);

    public void setPayService(PayService payService){
        this.payService = payService;
    }

    /**
     * 处理成功回调
     * @param payBackDto
     */
    protected void doProcessSuccess(PayBackDto payBackDto) {
        PayOrderDto payOrderDto = payService.getPayOrderFromBackParam(payBackDto.getParams(), payBackDto.getPayType());
        final String sn = payOrderDto.getOrderId();
        if (serialNumberUtils.isPaySn(sn)) {
            final Payment payment = paymentService.findByPaySn(sn);
            //支付单状态必须为待支付，才能进行以下操作
            if (PayStatus.WAIT_PAY.equals(payment.getStatus())) {
                //发送支付成功的消息
                sendPaidSuccessMessage(payment);
            }
        } else if (serialNumberUtils.isRechargeSn(sn)) {
            final Recharge recharge = rechargeService.findByRechargeSn(sn);
            //充值单状态必须为待支付，才能进行以下操作
            if (RechargeStatus.WAITING.equals(recharge.getStatus())) {
                //发送支付成功的消息（充值其实质也是支付）
                sendPaidSuccessMessage(recharge);
            }
        }
    }

    /**
     * 处理失败回调
     */
    protected void doProcessFailure(PayBackDto payBackDto) {
        PayOrderDto payOrderDto = payService.getPayOrderFromBackParam(payBackDto.getParams(), payBackDto.getPayType());
        final String sn = payOrderDto.getOrderId();
        if (serialNumberUtils.isPaySn(sn)) {
            final Payment payment = paymentService.findByPaySn(sn);
            //支付单状态必须为待支付，才能进行以下操作
            if (PayStatus.WAIT_PAY.equals(payment.getStatus())) {
                //发送支付单状态为付款失败的消息
                sendPaidFailureMessage(payment,payBackDto);
            }
        } else if (serialNumberUtils.isRechargeSn(sn)) {
            final Recharge recharge = rechargeService.findByRechargeSn(sn);
            //充值单状态必须为待支付，才能进行以下操作
            if (RechargeStatus.WAITING.equals(recharge.getStatus())) {
                //发送充值单状态为付款失败的消息
                sendPaidFailureMessage(recharge,payBackDto);
            }
        }
    }

    /**
     * 发送支付成功消息
     * @param payment
     */
    protected void sendPaidSuccessMessage(final Payment payment){
        final PayOrderMessageDto payOrderMessageDto = new PayOrderMessageDto();
        assemblePayOrderMessageDto(payment,payOrderMessageDto);

        payOrderMessageDto.setSuccess(true);
        payOrderMessageDto.setErrorMsg("");
        final byte[]  msgBody = ByteArrayStringUtils.getInstance().string2ByteArray(JSON.toJSONString(payOrderMessageDto),"UTF-8");
        Message message = new Message(topicPaycenterPay,PayConstants.PAY_TAG_PAY_SUCCESS,payment.getPaySn(),msgBody);
        //发送支付成功的事务消息
        SendResult sendResult = producer.send(message,payLocalTransactionExecuter,payment.getPaySn());
        log.info(sendResult.toString());
        //TODO 写到消息监听器里，记录施小包流水
        recordShiXiaoBaoTrx(payment);
    }



    /**
     * 发送支付成功消息(充值)
     * @param recharge
     */
    protected void sendPaidSuccessMessage(final Recharge recharge){
        final PayOrderMessageDto payOrderMessageDto = new PayOrderMessageDto();
        assemblePayOrderMessageDto(recharge,payOrderMessageDto);

        payOrderMessageDto.setSuccess(true);
        payOrderMessageDto.setErrorMsg("");
        final byte[]  msgBody = ByteArrayStringUtils.getInstance().string2ByteArray(JSON.toJSONString(payOrderMessageDto),"UTF-8");
        Message message = new Message(topicPaycenterPay,PayConstants.PAY_TAG_PAY_SUCCESS,recharge.getRSn(),msgBody);
        //发送支付成功的事务消息(充值)
        SendResult sendResult = producer.send(message,payLocalTransactionExecuter,recharge.getRSn());
        log.info(sendResult.toString());
        //TODO 放到消息监听器处理，记录施小包流水
        recordShiXiaoBaoTrx(recharge);
    }




    /**
     * 发送支付失败消息
     * @param payment
     */
    protected void sendPaidFailureMessage(final Payment payment,PayBackDto payBackDto){
        final PayOrderMessageDto payOrderMessageDto = new PayOrderMessageDto();
        assemblePayOrderMessageDto(payment, payOrderMessageDto);

        payOrderMessageDto.setSuccess(false);
        payOrderMessageDto.setErrorMsg(getPayErrorMsg(payBackDto));
        final byte[] msgBody = ByteArrayStringUtils.getInstance().string2ByteArray(JSON.toJSONString(payOrderMessageDto),"UTF-8");
        final Message message = new Message(topicPaycenterPay,PayConstants.PAY_TAG_PAY_FAILURE,payment.getPaySn(),msgBody);
        //发送支付失败的事务消息
        SendResult sendResult = producer.send(message,payLocalTransactionExecuter,payment.getPaySn());
        log.info(sendResult.toString());
    }

    /**
     * 发送充值失败消息
     * @param recharge
     */
    protected void sendPaidFailureMessage(final Recharge recharge,PayBackDto payBackDto){
        final PayOrderMessageDto payOrderMessageDto = new PayOrderMessageDto();
        assemblePayOrderMessageDto(recharge,payOrderMessageDto);
        payOrderMessageDto.setSuccess(false);
        payOrderMessageDto.setErrorMsg(getPayErrorMsg(payBackDto));
        final byte[] msgBody = ByteArrayStringUtils.getInstance().string2ByteArray(JSON.toJSONString(payOrderMessageDto),"UTF-8");
        final Message message = new Message(topicPaycenterPay,PayConstants.PAY_TAG_PAY_FAILURE,recharge.getRSn(),msgBody);
        //发送支付失败的事务消息
        SendResult sendResult = producer.send(message,payLocalTransactionExecuter,recharge.getRSn());
        log.info(sendResult.toString());
    }

    protected String ABCBankNotify(PayBackDto payBackDto) {
        String msg = payBackDto.getParams().get("MSG");
        PaymentResult tResult = null;
        String tMerchantPage = null;
        try {
            tResult = new PaymentResult(msg);
        } catch (TrxException e) {
            e.printStackTrace();
        }
        String OrderNo = tResult.getValue("OrderNo");//获得订单号
        String Amount = tResult.getValue("Amount");//获得订单金额
        PayOrderDto payOrderDto = new PayOrderDto();
        payOrderDto.setAmount(new BigDecimal(Amount));
        payOrderDto.setOrderId(OrderNo);
        if (serialNumberUtils.isRechargeSn(OrderNo)) {
            Recharge recharge = rechargeService.findByRechargeSn(OrderNo);
            tMerchantPage = recharge.getReturnUrl();
        } else {
            final Payment payment = paymentService.findByPaySn(OrderNo);
            tMerchantPage = payment.getReturnUrl();
        }
        BigDecimal PAYMENT = BigDecimal.valueOf(Double.valueOf(Amount));//转成我们自己数据库的类型
        //2、判断支付结果处理状态，进行后续操作
        if (tResult.isSuccess()) {
            doProcessSuccess(payBackDto);
            //3、支付成功并且验签、解析成功
//                payBackProcessService.doProcess(OrderNo);
//            tMerchantPage = "http://10.233.4.11:8080/ebusclient/ResultSuccess.jsp";//通知成功页面
        } else {
            //先取得错误的信息
            String returnCode = tResult.getReturnCode();
            String errorMessage = tResult.getErrorMessage();
            log.info("returnCode:" + returnCode + "errorMessage:" + errorMessage);
            //TODO:可以根据错误码和错误信息来判断是什么错误从而可以执行下一步的操作
            //如果tResult表示未成功，先去平台查询该笔订单的支付是否已经支付成功只是因为签名的问题导致返回的结果失败
            TransferQueryDto transferQueryDto = new TransferQueryDto();
            transferQueryDto.setOldRequestSn(OrderNo);
            transferQueryDto.setChannel(payBackDto.getChannel());
            transferQueryDto.setPayType(Constants.PAY_TYPE_DIRECTPAY);
            TransferQueryHandler transferQueryHandler = transferQueryDispatcher.getTransferQueryHandler(transferQueryDto.getChannel());
            TransferQueryVo transferQueryVo = transferQueryHandler.buildQuery(transferQueryDto);
            Map map = transferQueryHandler.getTransferService().sendTransferQuery(transferQueryVo);
            Map resultMap = (Map) map.get("data");
            String status = (String) resultMap.get("Status");
            if ("04".equals(status)) {
                doProcessSuccess(payBackDto);
//                tMerchantPage = "http://10.233.4.11:8080/ebusclient/ResultSuccess.jsp";
            } else {
                doProcessFailure(payBackDto);
                //4、支付成功但是由于验签或者解析报文等操作失败
//                tMerchantPage = "http://10.233.4.11:8080/ebusclient/ResultFail.jsp";//通知失败页面
            }
        }
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("<URL>").append(tMerchantPage).append("</URL>").
                append("<HTML>").append("<HEAD>").append("<meta http-equiv=\"refresh\" content=\"0; url='").append(tMerchantPage).append("'\">").append("</HEAD>").append("</HTML>");
        String url = stringBuilder.toString();
        return url;
    }

    private void recordShiXiaoBaoTrx(Payment payment) {
        ShiXiaoBaoTrx shiXiaoBaoTrx = new ShiXiaoBaoTrx();
        shiXiaoBaoTrx.setTradeSn(payment.getPaySn());
        shiXiaoBaoTrx.setAccountId(payment.getAccountId());
        shiXiaoBaoTrx.setUserCode(payment.getAccountUserCode());
        shiXiaoBaoTrx.setUserName(payment.getAccountRealName());
        shiXiaoBaoTrx.setOperName(payment.getOperName());
        shiXiaoBaoTrx.setType(FundFlowDirection.TRANSFER);
        if(StringUtils.isNotBlank(payment.getChannel())) {
            shiXiaoBaoTrx.setBankcardName(channelService.findChannelByChannelCode(payment.getChannel()).getBankName());
        }
        shiXiaoBaoTrx.setAmount(payment.getAmount());
        shiXiaoBaoTrxService.recordShixiaobaoTrx(shiXiaoBaoTrx);
    }

    private void recordShiXiaoBaoTrx(Recharge recharge) {
        ShiXiaoBaoTrx shiXiaoBaoTrx = new ShiXiaoBaoTrx();
        shiXiaoBaoTrx.setTradeSn(recharge.getRSn());
        shiXiaoBaoTrx.setAccountId(recharge.getAccountId());
        shiXiaoBaoTrx.setUserCode(recharge.getAccountUserCode());
        shiXiaoBaoTrx.setUserName(recharge.getAccountRealName());
        shiXiaoBaoTrx.setOperName(recharge.getOperName());
        shiXiaoBaoTrx.setType(FundFlowDirection.RECHARGE);
        shiXiaoBaoTrx.setBankcardName(channelService.findChannelByChannelCode(recharge.getChannel()).getBankName());
        shiXiaoBaoTrx.setAmount(recharge.getAmount());
        shiXiaoBaoTrxService.recordShixiaobaoTrx(shiXiaoBaoTrx);
    }

    /**
     * 组装支付消息
     *
     * @param payment
     * @param payOrderMessageDto
     */
    private void assemblePayOrderMessageDto(Payment payment, PayOrderMessageDto payOrderMessageDto) {
        payOrderMessageDto.setSn(payment.getPaySn());
        payOrderMessageDto.setAmount(payment.getAmount());
        payOrderMessageDto.setBizzSys(payment.getBizzSys());
        payOrderMessageDto.setBizzSn(payment.getBizzSn());
        payOrderMessageDto.setChannel(payment.getChannel());
        payOrderMessageDto.setPayType(payment.getPayType());
        payOrderMessageDto.setUserCode(payment.getAccountUserCode());
        payOrderMessageDto.setAccountId(payment.getAccountId());
        payOrderMessageDto.setRealName(payment.getAccountRealName());
        payOrderMessageDto.setAmount(payment.getAmount());
        payOrderMessageDto.setFinishedAt(payment.getFinishedAt());

        //设置交易主体与业务订单信息
        List<TradeOrder> tradeOrders = Lists.newArrayList();
        List<OrderDto> orderDtos = Lists.newArrayList();

        //如果是从账单发起的付款
        if(serialNumberUtils.isPayBizzSn(payment.getBizzSn())){
            PaymentToTradeOrder paymentToTradeOrder = paymentToTradeOrderService.findByPaySn(payment.getPaySn());
            List<String> orderSnList = Lists.newArrayList();
            orderSnList.add(paymentToTradeOrder.getOrderSn());
            tradeOrders = tradeOrderService.findOrdersByBizzSysAndOrderSns(paymentToTradeOrder.getBizzSys(),orderSnList);
        }else {
            List<String> orderSnList = paymentToTradeOrderService.selectOrderSnByPaySn(payment.getPaySn());
            tradeOrders = tradeOrderService.findOrdersByBizzSysAndOrderSns(payment.getBizzSys(), orderSnList);
        }
        for (TradeOrder tradeOrder : tradeOrders) {
            OrderDto orderDto = new OrderDto();
            org.springframework.beans.BeanUtils.copyProperties(tradeOrder, orderDto);
            orderDtos.add(orderDto);
        }
        payOrderMessageDto.setOrders(orderDtos);
    }

    private void assemblePayOrderMessageDto(Recharge recharge, PayOrderMessageDto payOrderMessageDto) {
        payOrderMessageDto.setSn(recharge.getRSn());
        payOrderMessageDto.setAmount(recharge.getAmount());
        payOrderMessageDto.setChannel(recharge.getChannel());
        payOrderMessageDto.setPayType(recharge.getPayType());
        payOrderMessageDto.setFinishedAt(recharge.getFinishedAt());
        payOrderMessageDto.setUserCode(recharge.getAccountUserCode());
        payOrderMessageDto.setAccountId(recharge.getAccountId());
        payOrderMessageDto.setRealName(recharge.getAccountRealName());
    }
}
